/*
 * 简单的日志实现
 */

package mylog

import (
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"time"
)

const (
	LOG_OFF = iota
	LOG_INFO
	LOG_DEBUG
	LOG_ERROR

	LOG_TERM = iota
	LOG_FILE
)

var gLogLevel = LOG_INFO
var gLogMode = LOG_TERM
var gIsFullFileName = true
var gLogSwitchMode = true
var gLogMaxFileSize = 1024 // KB
var gFilePath = ""         // 默认是 $HOME路径/mylog/日期
var gFileName = "mylog.log"

func init() {
	gFilePath = os.Getenv("HOME")
	if len(gFilePath) == 0 {
		gFilePath = "/tmp/"
	}
	gFilePath = fmt.Sprintf("%s/mylog", gFilePath)
}

/* 设置日志参数
 * level -- 日志默认等级(日志等级大于默认等级才会写日志)
 * mode -- 日志模式(终端打印/写文件)
 * size -- 日志大小(日志模式为写文件时有效)
 * is_full -- 是否打印文件名全路径
 * is_switch -- 日志大小超过默认大小时是否切换新日志(日志模式为写文件时有效)
 * path -- 日志路径(日志模式为写文件时有效)
 * name -- 日志文件名(日志模式为写文件时有效)
 */
func SetLogParams(level, mode, size int, is_full, is_switch bool, path, name string) {
	gLogLevel = level
	if mode == LOG_FILE {
		gLogMode = LOG_FILE
	}
	if gLogMode == LOG_FILE && size > 0 {
		gLogMaxFileSize = size
	}
	gIsFullFileName = is_full
	gLogSwitchMode = is_switch
	if len(path) > 0 {
		gFilePath = path
	}
	if len(name) > 0 {
		gFileName = name
	}
}

func WriteLog(level int, format string, a ...interface{}) {
	// 如果level比默认级别低，则不打印日志
	if gLogLevel == LOG_OFF || level < gLogLevel {
		return
	}
	var log_str string
	switch level {
	case LOG_INFO:
		log_str = "INFO"
	case LOG_DEBUG:
		log_str = "DEBUG"
	case LOG_ERROR:
		log_str = "ERROR"
	default:
		log_str = "OTHERS"
	}

	_, filename, fileline, ok := runtime.Caller(1)
	if !ok {
		filename = "-"
		fileline = 0
	}
	if !gIsFullFileName {
		filename = path.Base(filename)
	}

	now := time.Now()
	str := fmt.Sprintf("[%04d-%02d-%02d %02d:%02d:%02d:%03d][%s][%d][%s]%s\n",
		now.Year(),
		now.Month(),
		now.Day(),
		now.Hour(),
		now.Minute(),
		now.Second(),
		now.Nanosecond()/1000000,
		filename,
		fileline,
		log_str,
		format,
	)

	var file io.Writer = os.Stdout
	if gLogMode == LOG_FILE {
		logfile, err := openLogFile(now)
		if err != nil {
			fmt.Fprintf(os.Stderr, "openLogFile() error [%v]\n", err)
		} else {
			defer logfile.Close()
			file = logfile
		}
	}

	fmt.Fprintf(file, str, a...)
}

func openLogFile(now time.Time) (file *os.File, err error) {
	local_path := fmt.Sprintf("%s/%04d%02d%02d",
		gFilePath,
		now.Year(),
		now.Month(),
		now.Day(),
	)
	full_filename := fmt.Sprintf("%s/%s", local_path, gFileName)

	// 先打开日志，如果打开日志失败，创建目录，重新打开
	// 也可以通过 os.Stat(path) + os.IsNotExist(err) 判断目录是否存在
	logfile, err := os.OpenFile(full_filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		//fmt.Fprintf(os.Stderr, "OpenFile() error [%v]\n", err)
		err = os.MkdirAll(local_path, os.ModePerm)
		if err != nil {
			return nil, err
		}

		logfile, err = os.OpenFile(full_filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			return nil, err
		}
	}

	if gLogSwitchMode {
		fi, err := logfile.Stat()
		if fi.Size() > int64(gLogMaxFileSize) {
			logfile.Close() // 关闭旧文件
			bak_filename := fmt.Sprintf("%s_%04d%02d%02d_%02d%02d%02d%03d",
				full_filename,
				now.Year(),
				now.Month(),
				now.Day(),
				now.Hour(),
				now.Minute(),
				now.Second(),
				now.Nanosecond()/1000000,
			)
			if err = os.Rename(full_filename, bak_filename); err != nil {
				return nil, err
			}

			logfile, err = os.OpenFile(full_filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
			if err != nil {
				return nil, err
			}
		}
	}

	return logfile, err
}
